Disable C1 clock ramping on AMD 8th gen CPUs.
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 17 Feb 2006 19:41:30 +0000 (20:41 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 17 Feb 2006 19:41:30 +0000 (20:41 +0100)
This reverts a previous changeset that removed this code.
It is currently required for good TSC behaviour in SVM
(fully virtualised) guests.

Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/cpu/amd.c

index fe4b078aa8edea495bf2e5bbe64e61cd15bbc25b..80e3e6f4fde73063a9d8853d51929fc869ec1100 100644 (file)
@@ -42,6 +42,62 @@ custom_param("amd_flush_filter", flush_filter);
 extern void vide(void);
 __asm__(".text\n.align 4\nvide: ret");
 
+/* Can this system suffer from TSC drift due to C1 clock ramping? */
+static int c1_ramping_may_cause_clock_drift(struct cpuinfo_x86 *c) 
+{ 
+       if (c->x86 < 0xf) {
+               /*
+                * TSC drift doesn't exist on 7th Gen or less
+                * However, OS still needs to consider effects
+                * of P-state changes on TSC
+                */
+               return 0;
+       } else if (cpuid_edx(0x80000007) & (1<<8)) {
+               /*
+                * CPUID.AdvPowerMgmtInfo.TscInvariant
+                * EDX bit 8, 8000_0007
+                * Invariant TSC on 8th Gen or newer, use it
+                * (assume all cores have invariant TSC)
+                */
+               return 0;
+       }
+       return 1;
+}
+
+/* PCI access functions. Should be safe to use 0xcf8/0xcfc port accesses here. */
+static u8 pci_read_byte(u32 bus, u32 dev, u32 fn, u32 reg)
+{
+       outl((1U<<31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3), 0xcf8);
+       return inb(0xcfc + (reg & 3));
+}
+
+static void pci_write_byte(u32 bus, u32 dev, u32 fn, u32 reg, u8 val)
+{
+       outl((1U<<31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3), 0xcf8);
+       outb(val, 0xcfc + (reg & 3));
+}
+
+/*
+ * Disable C1-Clock ramping if enabled in PMM7.CpuLowPwrEnh on 8th-generation
+ * cores only. Assume BIOS has setup all Northbridges equivalently.
+ */
+static void disable_c1_ramping(void) 
+{
+       u8 pmm7;
+       int node;
+
+       for (node=0; node < NR_CPUS; node++) {
+               /* PMM7: bus=0, dev=0x18+node, function=0x3, register=0x87. */
+               pmm7 = pci_read_byte(0, 0x18+node, 0x3, 0x87);
+               /* Invalid read means we've updated every Northbridge. */
+               if (pmm7 == 0xFF)
+                       break;
+               pmm7 &= 0xFC; /* clear pmm7[1:0] */
+               pci_write_byte(0, 0x18+node, 0x3, 0x87, pmm7);
+               printk ("AMD: Disabling C1 Clock Ramping Node #%x\n", node);
+       }
+}
+
 static void __init init_amd(struct cpuinfo_x86 *c)
 {
        u32 l, h;
@@ -275,6 +331,10 @@ static void __init init_amd(struct cpuinfo_x86 *c)
        }
 #endif
 
+       /* Prevent TSC drift in non single-processor, single-core platforms. */
+       if ((smp_processor_id() == 1) && c1_ramping_may_cause_clock_drift(c))
+               disable_c1_ramping();
+
        start_svm();
 }